﻿using Architecture.CrossCutting.LoggingEntities;
using Architecture.CrossCutting.RepositoryPattern.Logging.ETW;
using Architecture.CrossCutting.WebApi.Tracing.ETW;
using Architecture.Logging.Agent.Configuration;
using Architecture.Logging.Agent.Services.Admin;
using Architecture.Logging.Agent.Services;
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session;
using Microsoft.Owin.Hosting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Architecture.Logging.Agent
{
    class Program
    {
        public static TextWriter Out = Console.Out;

        static void Main(string[] args)
        {


            WebApiListener server = new WebApiListener();
            server.Start();


            if (!(TraceEventSession.IsElevated() ?? false))
            {
                Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
                Debugger.Break();
                return;
            }


            var sessionName = "ArchitectureResearch";
            Out.WriteLine("Creating a '{0}' session", sessionName);
            Out.WriteLine("Use 'logman query -ets' to see active sessions.");
            Out.WriteLine("Use 'logman stop {0} -ets' to manually stop orphans.", sessionName);
            using (var session = new TraceEventSession(sessionName))
            {

                // Here we install the Control C handler.   It is OK if Dispose is called more than once.  
                Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
                {
                    session.Dispose();
                    server.Stop();
                };


                session.StopOnDispose = true;
                using (var source = new ETWTraceEventSource(session.SessionName, TraceEventSourceType.Session))
                {
                    Context.Current.Source = source;
                    var registeredParser = new RegisteredTraceEventParser(source);
                    registeredParser.All += delegate (TraceEvent data)
                    {
                        var log = ETWIISLogParser.Current.CreateFromTraceEvent(data);
                        using (var ctx = new LoggingServiceContext())
                        {
                            ctx.IISLogs.Add(log);
                            ctx.SaveChanges();
                        }
                        var taskName = data.TaskName;
                        System.Diagnostics.Debug.WriteLine(taskName);
                        System.Console.WriteLine(String.Format("{0} - {1}", taskName,data.FormattedMessage));
                    };

                    //Enable IIS Provider Logging Microsoft-Windows-IIS-Logging
                    session.EnableProvider(Guid.Parse("7E8AD27F-B271-4EA2-A783-A47BDE29143B"), TraceEventLevel.Always);
                    source.Process();
                }
            }

            server.Stop();
            return;



            using (var session = new TraceEventSession(sessionName))
            {

                // Here we install the Control C handler.   It is OK if Dispose is called more than once.  
                Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
                {
                    session.Dispose();
                };

                // To demonstrate non-trivial event manipulation, we calculate the time delta between 'MyFirstEvent and 'MySecondEvent'
                // firstEventTimeMSec remembers all the 'MyFirstEvent' arrival times (indexed by their ID)  
                var firstEventTimeMSec = new Dictionary<int, double>();

                /*****************************************************************************************************/
                // Hook up events.   To so this first we need a 'Parser. which knows how to part the events of a particular Event Provider.
                // In this case we get a DynamicTraceEventSource, which knows how to parse any EventSource provider.    This parser
                // is so common, that TraceEventSource as a shortcut property called 'Dynamic' that fetches this parsers.  

                // For debugging, and demo purposes, hook up a callback for every event that 'Dynamic' knows about (this is not EVERY
                // event only those know about by DynamiceTraceEventParser).   However the 'UnhandledEvents' handler below will catch
                // the other ones.
                //session.Source.Dynamic.All += delegate(TraceEvent data)
                //{
                //    // ETW buffers events and only delivers them after buffering up for some amount of time.  Thus 
                //    // there is a small delay of about 2-4 seconds between the timestamp on the event (which is very 
                //    // accurate), and the time we actually get the event.  We measure that delay here.     
                //    var delay = (DateTime.Now - data.TimeStamp).TotalSeconds;
                //    Out.WriteLine("GOT Event Delay={0:f1}sec: {1} ", delay, data.ToString());
                //};

                //var parser = new DynamicTraceEventParser(session.Source);
                //parser.All += delegate(TraceEvent data)
                //{
                //    // ETW buffers events and only delivers them after buffering up for some amount of time.  Thus 
                //    // there is a small delay of about 2-4 seconds between the timestamp on the event (which is very 
                //    // accurate), and the time we actually get the event.  We measure that delay here.     
                //    var delay = (DateTime.Now - data.TimeStamp).TotalSeconds;
                //    Out.WriteLine("GOT Event Delay={0:f1}sec: {1} ", delay, data.ToString());
                //};


                //session.Source.ObserveAll().




                session.Source.Dynamic.AddCallbackForProviderEvent("ArchitectureResearch-EF-Tracing", "DBQuery", delegate (TraceEvent data)
                {
                    var ctx = new LoggingServiceContext();
                    ctx.EFLogs.Add(ETWLogParser.Current.CreateFromEventDbQuery(data));
                    ctx.SaveChanges();

                });


                Action<TraceEvent> WriteWebApiTrace = new Action<TraceEvent>(data =>
                {

                    var ctx = new LoggingServiceContext();
                    ctx.WebApiLogs.Add(ETWWebApiLogParser.Current.CreateFromEventWebApi(data));
                    ctx.SaveChanges();
                });

                session.Source.Dynamic.AddCallbackForProviderEvent("ArchitectureResearch-Webapi-Tracing", "Request/Receive", WriteWebApiTrace);
                session.Source.Dynamic.AddCallbackForProviderEvent("ArchitectureResearch-Webapi-Tracing", "OpBegin/Start", WriteWebApiTrace);
                session.Source.Dynamic.AddCallbackForProviderEvent("ArchitectureResearch-Webapi-Tracing", "OpEnd/Stop", WriteWebApiTrace);
                session.Source.Dynamic.AddCallbackForProviderEvent("ArchitectureResearch-Webapi-Tracing", "Response/Reply", WriteWebApiTrace);


                // Add logic on what to do when we get "MyFirstEvent"
                session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-Demos-SimpleMonitor", "MyFirstEvent", delegate (TraceEvent data)
                {
                    // On First Events, simply remember the ID and time of the event
                    firstEventTimeMSec[(int)data.PayloadByName("MyId")] = data.TimeStampRelativeMSec;
                });

                // Add logic on what to do when we get "MySecondEvent"
                session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-Demos-SimpleMonitor", "MySecondEvent", delegate (TraceEvent data)
                {
                    // On Second Events, if the ID matches, compute the delta and display it. 
                    var myID = (int)data.PayloadByName("MyId");
                    double firstEventTime;
                    if (firstEventTimeMSec.TryGetValue(myID, out firstEventTime))
                    {
                        firstEventTimeMSec.Remove(myID);            // We are done with the ID after matching it, so remove it from the table. 
                        Out.WriteLine("   >>> Time Delta from first Event = {0:f3} MSec", data.TimeStampRelativeMSec - firstEventTime);
                    }
                    else
                        Out.WriteLine("   >>> WARNING, Found a 'SecondEvent' without a corresponding 'FirstEvent'");
                });

                // Add logic on what to do when we get "Stop"
                session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-Demos-SimpleMonitor", "MyStopEvent", delegate (TraceEvent data)
                {
                    Out.WriteLine("    >>> Got a stop message");
                    // Stop processing after we we see the 'Stop' event, this will 'Process() to return.   It is OK to call Dispose twice 
                    session.Source.Dispose();
                });

#if DEBUG
                // The callback above will only be called for events the parser recognizes (in the case of DynamicTraceEventParser, EventSources)
                // It is sometimes useful to see the other events that are not otherwise being handled.  The source knows about these and you 
                // can ask the source to send them to you like this.  
                session.Source.UnhandledEvents += delegate (TraceEvent data)
                {
                    if ((int)data.ID != 0xFFFE)         // The EventSource manifest events show up as unhanded, filter them out.
                        Out.WriteLine("GOT UNHANDLED EVENT: " + data.Dump());
                };
#endif
                // At this point we have created a TraceEventSession, hooked it up to a TraceEventSource, and hooked the
                // TraceEventSource to a TraceEventParser (you can do several of these), and then hooked up callbacks
                // up to the TraceEventParser (again you can have several).  However we have NOT actually told any
                // provider (EventSources) to actually send any events to our TraceEventSession.  
                // We do that now.  

                // Enable my provider, you can call many of these on the same session to get events from other providers.  
                // Because this EventSource did not define any keywords, I can only turn on all events or none.  
                var restarted = session.EnableProvider("Microsoft-Demos-SimpleMonitor");

                //session.EnableProvider("ArchitectureResearch-Debug-Data", TraceEventLevel.Always);
                //session.EnableProvider("ArchitectureResearch-EF-Tracing", TraceEventLevel.Always);
                //session.EnableProvider("ArchitectureResearch-Webapi-Tracing", TraceEventLevel.Always);

                //Enable IIS Provider Logging Microsoft-Windows-IIS-Logging
                session.EnableProvider(Guid.Parse("7E8AD27F-B271-4EA2-A783-A47BDE29143B"), TraceEventLevel.Always);

                //session.EnableProvider("Microsoft-Windows-IIS-Logging");

                if (restarted)      // Generally you don't bother with this warning, but for the demo we do. 
                    Out.WriteLine("The session {0} was already active, it has been restarted.", sessionName);

                Out.WriteLine("**** Start listening for events from the Microsoft-Demos-SimpleMonitor provider.");
                // go into a loop processing events can calling the callbacks.  Because this is live data (not from a file)
                // processing never completes by itself, but only because someone called 'source.Dispose()'.  
                session.Source.Process();
                Out.WriteLine();
                Out.WriteLine("Stopping the collection of events.");
            }

            Console.WriteLine("Ready to listen events");
            Console.ReadLine();
            Console.WriteLine("End =============================");
            Console.ReadLine();

        }
    }
}
